https://mp.weixin.qq.com/s/oVkCCaCSP5ntZyenl-5Y6A

<!-- 前端路由 -->
前端路由是指在浏览器端控制页面内容切换显示的机制。
在没有服务器端参与的情况下，前端路由可以根据URL的变化，对应展现不同的内容，实现页面的“伪”跳转。

<!-- hash和history路由模式-->

<!-- hash模式原理： -->
1.浏览器原生支持通过window.location.hash读写URL中的hash值，并且当hash值变化时，页面不会触发重新加载。
2.SPA可以监听hashchange事件，在URL的hash部分变化时根据定义好的路由映射关系来动态渲染内容。
3.早期的前端路由的实现就是基于location.hash来实现的，location.hash的值就是URL中#后面的内容 
其实现原理就是监听#后面的内容来发起Ajax请求来进行局部更新，而不需要刷新整个页面。
4.使用hashchange事件来监听 URL 的变化，以下这几种情况改变 URL 都会触发 hashchange 事件：
浏览器前进后退改变 URL、<a>标签改变 URL、window.location改变URL。
<script>
  // Hash模式的简易实现
  window.addEventListener('hashchange', routeChange);
  function routeChange() {
    const hash = window.location.hash.slice(1); // Remove the '#' symbol
    // 基于hash值显示不同内容
    routerView.innerHTML = routes[hash] ? routes[hash] : routes['404'];
  }
</script>

注意：我使用了vue中的router.push,发现没有触发hashchange事件, 
这是因为hashchange是浏览器的事件,push是vue内部的机制在处理路由变化

<!-- hash模式的优缺 -->
兼容低版本浏览器，Angular1.x和Vue默认使用的就是hash路由
只有#符号之前的内容才会包含在请求中被发送到后端，也就是说就算后端没有对路由全覆盖，但是不会返回404错误
hash值的改变，都会在浏览器的访问历史中增加一个记录，所以可以通过浏览器的回退、前进按钮控制hash的切换
会覆盖锚点定位元素的功能
不太美观，#后面传输的数据复杂的话会出现问题

<!-- History模式原理： -->
1.History API 允许SPA在浏览历史记录中添加、修改记录而不会触发页面加载。
2.通过history.pushState和history.replaceState可以改变URL且不重新加载页面。
3.SPA可以监听 popstate 事件来响应浏览器前进、后退操作。
4.history 提供了 pushState 和 replaceState 两个方法来记录路由状态，这两个方法改变 URL 不会引起页面刷新
5.history 提供类似 hashchange 事件的 popstate 事件，但 popstate 事件有些不同：
通过浏览器前进后退改变 URL 时会触发 popstate 事件，
通过pushState/replaceState或<a>标签改变 URL 不会触发 popstate 事件。
好在我们可以拦截 pushState/replaceState的调用和<a>标签的点击事件来检测 URL 变化，
所以监听 URL 变化可以实现，只是没有 hashchange 那么方便。
6.pushState(state, title, url) 和 replaceState(state, title, url)都可以接受三个相同的参数：
state：需要保存的数据，这个数据在触发popstate事件时，可以在event.state里获取
title：标题，基本没用，一般传 null
url：设定新的历史记录的 url，新的 url 与当前 url 的 origin 必须是一样的，
否则会抛错，url可以是绝对路径，也可以是相对路径。
<script>
  // History模式的简易实现
  window.addEventListener('popstate', routeChange);
  function navigate(path) {
    history.pushState({}, "", path);
    routeChange();
  }
  function routeChange() {
    const path = window.location.pathname;
    // 根据pathname来渲染不同的页面组件
    routerView.innerHTML = routes[path] ? routes[path] : routes['404'];
  }

  // navigate('/user'); // 导航至用户页面
</script>

<!-- history模式优缺点 -->
history路由模式是一种前端路由模式，它使用浏览器的History API来管理URL的变化和页面的跳转。
相比于传统的hash路由模式，history路由模式具有以下优点和缺点：
优点：
1. 更美观的URL：history路由模式使用真实的URL路径，不带有#符号，更加美观和直观。
这对于用户来说更加友好，也有利于搜索引擎优化（SEO）。
2. 更好的用户体验：history路由模式可以实现无刷新的页面跳转，
用户在浏览网页时不会看到页面的刷新，提供了更流畅的用户体验。
3. 更好的浏览器兼容性：History API是HTML5的一部分，
现代浏览器都支持该API，因此history路由模式在大多数现代浏览器中都能正常工作。
缺点：
1. 服务器配置要求：history路由模式需要服务器进行相应的配置，以确保在直接访问URL时能够正确返回对应的页面。
这对于一些静态服务器或不支持URL重写的服务器来说可能需要额外的配置。
2. 刷新问题：当用户刷新页面时，浏览器会向服务器发送请求，如果服务器没有正确配置，
会导致404错误。为了解决这个问题，需要在服务器端进行相应的配置，以确保在刷新页面时能够正确返回对应的页面。
3. 兼容性问题：虽然现代浏览器都支持History API，但是在一些旧版本的浏览器中可能不支持或支持有限。
如果需要支持旧版本浏览器，可能需要进行额外的兼容性处理。

<!-- 关于刷新404的问题 为什么history模式下会出现?  为什么hash模式下不会出现? -->

<!-- 为什么history模式下会出现? -->

根据nginx的配置,当我们在地址栏输入 http://www.xxx.com 时，
这时会打开我们 dist 目录下的 index.html 文件，然后我们再跳转路由进入到 http://www.xxx.com/login
关键在这里，当我们在 http://website.com/login 页执行刷新操作,
会向真正的服务器发送请求资源，nginx location 是没有相关配置的，所以就会出现 404 的情况

<!-- 为什么hash模式下不会出现? -->
router hash 模式我们都知道是用符号#表示的，如 http://website.com/#/login, hash 的值为 #/login
它的特点在于：hash 虽然出现在 URL 中，但不会被包括在内 HTTP 请求中，对服务端完全没有影响，
因此改变 hash 不会重新加载页面
hash 模式下，仅 hash 符号之前的内容会被包含在请求中，如 http://website.com/#/login ...
只有 http://website.com 会被包含在请求中 ，因此对于服务端来说，即使没有配置location，也不会返回404错误

简单来说: 前端打包后的 dist 包中，只有 index.html。所以，history 模式下发送的请求地址，服务端是找不到的。
hash 模式：只将 hash 前面的部分当作地址
history 模式：会将地址栏中的地址全部看作请求地址

<!-- 在学习路由之前首先要了解一下SPA单页面应用-->
SPA（ single-page application ）仅在 Web 页面初始化时加载相应的 HTML、JavaScript 和 CSS。
一旦页面加载完成，SPA 不会因为用户的操作而进行页面的重新加载或跳转；
取而代之的是利用路由机制实现 HTML 内容的变换，UI 与用户的交互，避免页面的重新加载。

其实就是说，我们点击页面上的一些东西，并没有真正的发送请求进行页面跳转，而是在组件之间切换而已，仅仅刷新局部资源。

我们熟知的JS框架如react,vue,angular,ember都属于SPA

<!-- 与之对应的是多页面应用，他们的区别如下 -->
对比项\模式               SPA                                      MPA
结构        一个主页面+许多模块的组件                              许多完整的页面
体验       页面切换快,体验佳;当初次加载文件过多时,需要做相关的调优。    页面切换慢,网速慢的时候,体验尤其不好
资源文件     组件公用的资源只需要加载一次                            每个页面都要自己加载公用的资源
适用场景   对体验度和流畅度有较高要求的应用,不利于SEO(可借助SSR优化)     适用于对SEO要求较高的应用SEO  
内容更新    相关组件的切换,即局部更新                             整体HTML的切换,费钱(重复HTTP请求)
路由模式    可以使用hash,也可以使用history                        普通链接跳转
数据传递     因为单页面,使用全局变量就好（Vuex, Redux）            cookie、localStorage等缓存方案,URL参数,调用接口保存等
相关成本     前期开发成本较高,后期维护较为容易                      前期开发成本低,后期维护就比较麻烦,因为可能一个功能需要改很多地方
<!-- 优点： -->
用户体验好、快，内容的改变不需要重新加载整个页面，避免了不必要的跳转和重复渲染
基于上面一点，SPA 相对对服务器压力小
前后端职责分离，架构清晰，前端进行交互逻辑，后端负责数据处理

<!-- 缺点 -->
1.初次加载耗时多：为实现单页 Web 应用功能及显示效果，
需要在加载页面的时候将 JavaScript、CSS 统一加载，部分页面按需加载。
2.SEO 难度较大：由于所有的内容都在一个页面中动态替换显示，所以在 SEO 上其有着天然的弱势。